package scatter.comment.rest.subject.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.util.Assert;
import scatter.comment.pojo.hierarchical.po.CommentHierarchical;
import scatter.comment.pojo.subject.form.CommentSubjectAvailablePageQueryForm;
import scatter.comment.pojo.subject.param.CommentSubjectAddParam;
import scatter.comment.pojo.subject.po.CommentSubject;
import scatter.comment.pojo.subject.po.CommentSubjectStar;
import scatter.comment.pojo.subject.vo.CommentSubjectAvailableVo;
import scatter.comment.pojo.subject.vo.CommentSubjectVo;
import scatter.comment.rest.componentext.CommentSubjectAvailableExtProvider;
import scatter.comment.rest.componentext.CommentSubjectOnAddListener;
import scatter.comment.rest.hierarchical.mapstruct.CommentHierarchicalMapStruct;
import scatter.comment.rest.hierarchical.service.ICommentHierarchicalService;
import scatter.comment.rest.subject.mapper.CommentSubjectMapper;
import scatter.comment.rest.subject.mapstruct.CommentSubjectMapStruct;
import scatter.comment.rest.subject.mapstruct.CommentSubjectStarMapStruct;
import scatter.comment.rest.subject.service.ICommentSubjectPicService;
import scatter.comment.rest.subject.service.ICommentSubjectService;
import scatter.comment.rest.subject.service.ICommentSubjectStarService;
import scatter.common.pojo.form.BasePageQueryForm;
import scatter.common.rest.service.IBaseAddUpdateQueryFormServiceImpl;
import org.springframework.stereotype.Service;
import scatter.comment.pojo.subject.form.CommentSubjectAddForm;
import scatter.comment.pojo.subject.form.CommentSubjectUpdateForm;
import scatter.comment.pojo.subject.form.CommentSubjectPageQueryForm;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
 * <p>
 * 主体评论表 服务实现类
 * </p>
 *
 * @author yw
 * @since 2021-02-09
 */
@Service
@Transactional
public class CommentSubjectServiceImpl extends IBaseAddUpdateQueryFormServiceImpl<CommentSubjectMapper, CommentSubject, CommentSubjectAddForm, CommentSubjectUpdateForm, CommentSubjectPageQueryForm> implements ICommentSubjectService {

    @Autowired(required = false)
    private List<CommentSubjectOnAddListener> commentSubjectOnAddListenerList;

    @Autowired
    private ICommentSubjectPicService iCommentSubjectPicService;


    @Autowired
    private ICommentSubjectStarService commentSubjectStarService;

    @Qualifier("commonDbTaskExecutor")
    @Autowired
    private ExecutorService executorService;

    @Autowired(required = false)
    private List<CommentSubjectAvailableExtProvider> commentSubjectAvailableExtProviders;

    @Autowired
    private ICommentHierarchicalService iCommentHierarchicalService;


    @Override
    public void preAdd(CommentSubjectAddForm addForm,CommentSubject po) {
        super.preAdd(addForm,po);

    }

    @Override
    public void preUpdate(CommentSubjectUpdateForm updateForm,CommentSubject po) {
        super.preUpdate(updateForm,po);

    }

    @Override
    public Map<String, Page<CommentSubject>> listLatestCommentSubjectForPerSubject(List<String> subjectIds, Integer count, String groupFlag) {

        Assert.notEmpty(subjectIds,"subjectIds 不能为空");
        Assert.notNull(count,"count 不能为空");


        Map<String,Page<CommentSubject>> result = new HashMap<>();

        CompletableFuture[] completableFutures = subjectIds.stream().map(momentId -> CompletableFuture.supplyAsync(() -> {
            Page page = convertPage(new BasePageQueryForm().setCurrent(1L).setSize(Integer.toUnsignedLong(count)));
            page.setSearchCount(false);
            Page page1 = page(page, Wrappers.<CommentSubject>lambdaQuery()
                    .eq(CommentSubject::getSubjectId, momentId)
                    .eq(CommentSubject::getIsDisabled, false)
                    .eq(!isStrEmpty(groupFlag), CommentSubject::getGroupFlag, groupFlag)
                    .orderByDesc(CommentSubject::getTimeAt));
            return page1;
        },executorService).whenComplete((r,e)->{result.put(momentId, (Page<CommentSubject>)r);})).toArray(CompletableFuture[]::new);
        CompletableFuture.allOf(completableFutures).join();
        return result;
    }

    @Override
    public CommentSubject addCommentSubject(CommentSubjectAddParam addParam) {

        CommentSubject commentSubject = CommentSubjectMapStruct.INSTANCE.addParamToPo(addParam);
        commentSubject.setCommentCount(0);
        // 楼层默认 1
        commentSubject.setFloor(Optional.ofNullable(addParam.getFloor()).orElse(1));
        commentSubject.setIsDisabled(false);
        commentSubject.setStarCount(0);
        commentSubject.setTimeAt(LocalDateTime.now());
        // 添加前调用
        if (!isEmpty(commentSubjectOnAddListenerList)) {
            for (CommentSubjectOnAddListener commentSubjectOnAddListener : commentSubjectOnAddListenerList) {
                commentSubjectOnAddListener.preCommentSubjectAdd(addParam,commentSubject);
            }
        }

        save(commentSubject);

        if (!isEmpty(addParam.getCommentSubjectPicUrls())) {
            iCommentSubjectPicService.addPics(addParam.getCommentSubjectPicUrls(), commentSubject.getId());
        }

        // 添加后监听
        if (!isEmpty(commentSubjectOnAddListenerList)) {
            for (CommentSubjectOnAddListener commentSubjectOnAddListener : commentSubjectOnAddListenerList) {
                commentSubjectOnAddListener.postCommentSubjectAdd(addParam,commentSubject);
            }
        }
        return commentSubject;
    }

    @Override
    public CommentSubjectAvailableVo detailById(String id, String groupFlag, String loginUserId) throws InterruptedException {

        CommentSubject byId = getById(id);
        CommentSubjectAvailableVo commentSubjectAvailableVo = CommentSubjectMapStruct.INSTANCE.poToCommentSubjectAvailableVo(byId);
        fill(newArrayList(commentSubjectAvailableVo),groupFlag,loginUserId);

        return commentSubjectAvailableVo;
    }

    @Override
    public IPage<CommentSubjectAvailableVo> listAvailablePage(CommentSubjectAvailablePageQueryForm pageQueryForm,String loginUserId) throws InterruptedException {
        Page<CommentSubject> page = page(convertPage(pageQueryForm), Wrappers.<CommentSubject>lambdaQuery()
                .eq(CommentSubject::getSubjectId, pageQueryForm.getSubjectId())
                .eq(CommentSubject::getIsDisabled, false)
                .eq(!isStrEmpty(pageQueryForm.getGroupFlag()), CommentSubject::getGroupFlag, pageQueryForm.getGroupFlag())
                .orderByDesc(CommentSubject::getTimeAt).orderByDesc(CommentSubject::getId));

        IPage<CommentSubjectAvailableVo> commentSubjectAvailableVoIPage = CommentSubjectMapStruct.INSTANCE.pagePoToCommentSubjectAvailableVo(page);
        fill(commentSubjectAvailableVoIPage.getRecords(),pageQueryForm.getGroupFlag(),loginUserId);

        return commentSubjectAvailableVoIPage;

    }



    /**
     * 填充额外信息
     * @param commentSubjectVos
     * @param groupFlag
     * @param loginUserId
     */
    private void fill(List<CommentSubjectAvailableVo> commentSubjectVos, String groupFlag, String loginUserId) throws InterruptedException {

        int num = 3;
        if (!isEmpty(commentSubjectAvailableExtProviders)) {
            num += commentSubjectAvailableExtProviders.size();
        }

        // 下面是添加异步执行，加快提高性能
        CountDownLatch countDownLatch = new CountDownLatch(num);
        executorService.execute(()->{
            try {
                fillHasStared(commentSubjectVos,loginUserId);
            } finally {
                countDownLatch.countDown();
            }
        });
        executorService.execute(()->{
            try {
                fillLatestCommentSubjectStar(commentSubjectVos,10);
            } finally {
                countDownLatch.countDown();
            }
        });
        executorService.execute(()->{
            try {
                fillLatestCommentHierarchical(commentSubjectVos,5,groupFlag);
            } finally {
                countDownLatch.countDown();
            }
        });
        if (!isEmpty(commentSubjectAvailableExtProviders)) {
            for (CommentSubjectAvailableExtProvider commentSubjectAvailableExtProvider : commentSubjectAvailableExtProviders) {
                executorService.execute(()->{
                    try {
                        commentSubjectAvailableExtProvider.provideCommentSubject(commentSubjectVos);
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
        }
        countDownLatch.await();
    }

    /**
     * 填充是否已点赞
     * @param commentSubjectVos
     * @param loginUserId
     */
    private void fillHasStared(List<CommentSubjectAvailableVo> commentSubjectVos,String loginUserId){
        if (isEmpty(commentSubjectVos) || loginUserId == null) {
            return;
        }
        Map<String, Boolean> stringBooleanMap = commentSubjectStarService.hasStared(commentSubjectVos.stream().map(CommentSubjectVo::getId).collect(Collectors.toList()), loginUserId);
        commentSubjectVos.forEach(item -> item.setIsMeHasStared(stringBooleanMap.get(item.getId())));

    }

    /**
     * 填充最新点赞数据
     * @param commentSubjectVos
     * @param count
     */
    private void fillLatestCommentSubjectStar(List<CommentSubjectAvailableVo> commentSubjectVos,Integer count){
        if (isEmpty(commentSubjectVos)) {
            return;
        }
        Map<String, Page<CommentSubjectStar>> stringPageMap = commentSubjectStarService.listLatestStarForPerCommentSubject(commentSubjectVos.stream().map(CommentSubjectVo::getId).collect(Collectors.toList()), count);
        for (CommentSubjectAvailableVo item : commentSubjectVos) {
            IPage iPage = CommentSubjectStarMapStruct.INSTANCE.pagePoToVo(stringPageMap.get(item.getId()));
            item.setLatestCommentSubjectStars(iPage);
        }
    }
    /**
     * 填充盖楼评论数据
     * @param commentSubjectVos
     * @param count
     */
    private void fillLatestCommentHierarchical(List<CommentSubjectAvailableVo> commentSubjectVos, Integer count, String groupFlag){
        if (isEmpty(commentSubjectVos)) {
            return;
        }
        Map<String, Page<CommentHierarchical>> stringPageMap =
                iCommentHierarchicalService.listLatestCommentHierarchicalForPerSubject(commentSubjectVos.stream().map(CommentSubjectVo::getId).collect(Collectors.toList()), count,groupFlag);
        for (CommentSubjectAvailableVo item : commentSubjectVos) {
            IPage iPage = CommentHierarchicalMapStruct.INSTANCE.pagePoToVo(stringPageMap.get(item.getId()));
            item.setLatestCommentHierarchicals(iPage);
        }
    }

}
